package com.sunyard.suneam.core.util;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
 * @author zyl
 * @description
 * @since 2025/7/15
 */
public class RedisDistributedLockUtils {

    private final RedisTemplate<String, Object> redisTemplate;
    private final String lockKey;
    private final String lockValue;
    private final long expireTime;
    private final TimeUnit timeUnit;
    private volatile ScheduledFuture<?> renewalTask;

    // 使用单例线程池(实际项目建议从外部注入)
    private static final ScheduledExecutorService SCHEDULER =
            Executors.newScheduledThreadPool(4, r -> {
                Thread t = new Thread(r, "RedisLock-Renewal-Thread");
                t.setDaemon(true);
                return t;
            });

    // 解锁Lua脚本(保证原子性)
    private static final RedisScript<Long> UNLOCK_SCRIPT = new DefaultRedisScript<>(
            "local currentValue = redis.call('get', KEYS[1]); " +
                    "if currentValue == ARGV[1] then " +
                    "    redis.call('del', KEYS[1]); " +
                    "    return 1; " +
                    "elseif string.find(currentValue, ARGV[1]..':') == 1 then " +
                    "    local counter = tonumber(string.sub(currentValue, string.len(ARGV[1]) + 2)) - 1; " +
                    "    if counter > 0 then " +
                    "        redis.call('set', KEYS[1], ARGV[1]..':'..counter, 'px', ARGV[2]); " +
                    "        return 1; " +
                    "    else " +
                    "        redis.call('del', KEYS[1]); " +
                    "        return 1; " +
                    "    end " +
                    "else " +
                    "    return 0; " +
                    "end",
            Long.class
    );

    public RedisDistributedLockUtils(RedisTemplate<String, Object> redisTemplate,
                                     String lockKey,
                                     long expireTime,
                                     TimeUnit timeUnit) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey;
        this.lockValue = UUID.randomUUID().toString();
        this.expireTime = expireTime;
        this.timeUnit = timeUnit;
    }

    public boolean tryLock() {
        return tryLock(0, TimeUnit.MILLISECONDS);
    }

    public boolean tryLock(long timeout, TimeUnit unit) {
        long startTime = System.currentTimeMillis();
        long timeoutMillis = unit.toMillis(timeout);

        while (true) {
            // 检查是否可重入
            Object currentValue = redisTemplate.opsForValue().get(lockKey);
            if (currentValue != null) {
                String currentStr = currentValue.toString();
                if (currentStr.startsWith(lockValue + ":")) {
                    // 重入: 计数器+1
                    int counter = Integer.parseInt(currentStr.substring(currentStr.lastIndexOf(":") + 1));
                    boolean success = redisTemplate.opsForValue().setIfAbsent(
                            lockKey,
                            lockValue + ":" + (counter + 1),
                            expireTime,
                            timeUnit
                    );
                    if (success) {
                        scheduleRenewal();
                        return true;
                    }
                }
            }

            // 尝试获取新锁
            Boolean acquired = redisTemplate.opsForValue().setIfAbsent(
                    lockKey,
                    lockValue + ":1",  // 初始计数为1
                    expireTime,
                    timeUnit
            );

            if (Boolean.TRUE.equals(acquired)) {
                scheduleRenewal();
                return true;
            }

            // 超时判断
            if (System.currentTimeMillis() - startTime >= timeoutMillis) {
                return false;
            }

            // 短暂休眠后重试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
    }

    public void unlock() {
        // 执行Lua脚本保证原子性
        redisTemplate.execute(
                UNLOCK_SCRIPT,
                Collections.singletonList(lockKey),
                lockValue,
                String.valueOf(timeUnit.toMillis(expireTime))
        );
        // 取消续期
        cancelRenewal();
    }

    private void scheduleRenewal() {
        if (renewalTask != null) {
            renewalTask.cancel(false);
        }

        renewalTask = SCHEDULER.scheduleAtFixedRate(() -> {
            try {
                // 只有锁还存在且属于当前实例时才续期
                Object currentValue = redisTemplate.opsForValue().get(lockKey);
                if (currentValue != null && currentValue.toString().startsWith(lockValue)) {
                    redisTemplate.expire(lockKey, expireTime, timeUnit);
                }
            } catch (Exception e) {
                // 续期失败不影响主流程
            }
        }, expireTime / 3, expireTime / 3, timeUnit);
    }

    private void cancelRenewal() {
        if (renewalTask != null) {
            renewalTask.cancel(false);
            renewalTask = null;
        }
    }

    public long getRemainingTime() {
        return redisTemplate.getExpire(lockKey, TimeUnit.MILLISECONDS);
    }

    // 清理资源(实际项目中应该由Spring等容器管理)
    public static void shutdown() {
        SCHEDULER.shutdown();
    }
}
